﻿/* Program "SharpConsole"
 * Developers: Scott Ketelaar, <add your name here>
 */

/*TODO:
*Implement rest of commands.
*Implement system-path searching.
*Implement help for all commands.
 */

/*SharpConsole Program module
 * Contains the basic IO operations for the program
 * 
 * Original Author: Scott Ketelaar
 * Original Creation: 3/15/2011
 * 
 * Most Recent Update: 3/15/2011 by Scott Ketelaar
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpConsole
{
    class MainProgram
    {
        internal static string Prompt = ">"; //The "seperator", e.g. in "C:\WINDOWS>", would be the ">"
        private static char[] delimeters = new char[] { ' ' };
        internal static bool NormSwitches = true;
        internal static bool WarnArgs = true;
        internal static bool AutoCorrectArgs = false;
        internal static bool UseAdvTab = false;
        

        #region XMLdata
        /// <summary>
        /// Main Program Logic. Should not be called internally
        /// </summary>
        /// <param name="args"></param>
        #endregion
        static void Main(string[] args)
        {
        	Console.WriteLine("Welcome to SharpConsole, an open source Windows Command Prompt alternative\nusing the NET framework and coded in C#.");
 
            string UserCommand = "";
            string[] UserArgs;
            

            VirtualCMD.BG = Console.BackgroundColor;
            VirtualCMD.FG = Console.ForegroundColor;
            while (UserCommand.ToLower() != "exit")
            {
                try
                {
                    UserCommand = GetInput(null).Trim();
                    UserArgs = Utils.GetArgs(UserCommand, out UserCommand, NormSwitches, WarnArgs,AutoCorrectArgs);
                    if (!VirtualCMD.DoCommand(UserCommand, UserArgs))
                    {
                        //OK, so the command is not a built-in one. Lets try to run it:
                        try
                        {
                        	ExecuteShell(UserCommand, UserArgs, true, "", new System.Security.SecureString());
                        }
                        catch (Exception RunException)
                        {
                            Console.WriteLine("An error occured while trying to run {0}. Error details: {1}", UserCommand, RunException.Message);
                        }
                    }
                }
                catch (Exception MajorException)
                {
                    //This is a _MAJOR_ exception if it's caught with this Try. 
                    //We can probably still run though, so just print out exception details.
                    Console.WriteLine("Major exception occured:\n{0}", MajorException.ToString());
                }
            }

            Environment.Exit(0); //Exit program
        } //End of Main

        #region XMLdata
        /// <summary>
        /// Displays a prompt and returns user input
        /// </summary>
        /// <param name="PromptMessage">The prompt to display, or NULL to use the current directory</param>
        /// <returns>A System.String containing the user's response</returns>
        #endregion
        internal static string GetInput(string PromptMessage)
        {
            Console.WriteLine(); //New Line for Space....
            if (PromptMessage == null)
            { //We want to use the Current Directory
                Console.Write(Environment.CurrentDirectory + Prompt);
                PromptMessage = Environment.CurrentDirectory + Prompt;
            }
            else
            { //We want to use the supplied prompt
                Console.Write(PromptMessage + Prompt);
                PromptMessage = PromptMessage + Prompt;
            }
            if (UseAdvTab) {
            #region This hides the console input code, including the logic for tabs. DON'T MESS IT UP! Took forever to get right.
            string InputString, OldString = "!";
            InputString = "";
            int PrevLen = 0;
            List<string> FileHelper = new List<string>();
            List<string>.Enumerator en = FileHelper.GetEnumerator();
            while (true)
            {
                if (Console.KeyAvailable)
                {
                    ConsoleKeyInfo InKey = Console.ReadKey(true);
                    switch (InKey.Key)
                    {
                        case ConsoleKey.Tab:
                            if (PrevLen != 0)
                            {
                                for (int ix = 0; ix < PrevLen; ++ix)
                                {
                                    if (Console.CursorLeft <= PromptMessage.Length)
                                        break;
                                    Console.Write("\b \b");
                                    InputString = InputString.Remove(InputString.Length - 1);
                                }
                            }
                            if (InputString != OldString)
                            {
                                FileHelper = Utils.MatchFile(InputString);
                                en.Dispose();
                                en = FileHelper.GetEnumerator();
                                OldString = InputString;
                                PrevLen = 0;
                            }

                                en.MoveNext();
                            if (en.Current == null)
                            {
                                en.Dispose();
                                en = FileHelper.GetEnumerator();
                                en.MoveNext();
                            }

                            if (en.Current != null)
                            {
                                if (InputString.Length != 0)
                                {
                                    InputString = Utils.MeshStrings(InputString, en.Current);
                                    PrevLen = en.Current.Length;
                                }
                                else
                                {
                                    InputString = en.Current;
                                    PrevLen = en.Current.Length;
                                }
                                for (int x = Console.CursorLeft; x > PromptMessage.Length; x--)
                                    Console.Write("\b \b");
                            Console.Write(InputString);
                            }
                         break;
                        case ConsoleKey.Backspace:
                            if (Console.CursorLeft <= PromptMessage.Length)
                                break;
                            InputString = InputString.Remove(InputString.Length - 1);
                            Console.Write("\b \b");
                            PrevLen = 0;
                             break;
                        case ConsoleKey.Enter:
                             Console.WriteLine();
                             en.Dispose();
                                return InputString;                        
                        default:
                            for (int x = Console.CursorLeft; x > PromptMessage.Length; x--)
                                Console.Write("\b \b");

                         PrevLen = 0;
                         InputString += InKey.KeyChar.ToString();
                         Console.Write(InputString);
                         break;

                    }
                }
            }
            #endregion

            }
            else
            {
            	return Console.ReadLine();
            }
        }// End of GetInput

        #region XMLdata
        /// <summary>
        /// Runs a file in a new window without arguments. Equivalent to "Shell" command in VB.
        /// </summary>
        /// <param name="cmd">The name of the file to run</param>
        // Authors: Scott Ketelaar
        #endregion
        internal static void ExecuteShell(string cmd)
        {
            System.Diagnostics.Process P = new System.Diagnostics.Process();
            P.StartInfo.FileName = cmd;
            P.StartInfo.UseShellExecute = true;
            P.StartInfo.CreateNoWindow = false;
            P.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
            try
            {
                P.Start();
            }
            catch (Exception EX)
            {
                Console.WriteLine(EX.ToString());
            }
        }
        #region XMLdata
        /// <summary>
        /// Runs a file in a new window with arguments. Equivalent to "Shell" command in VB.
        /// </summary>
        /// <param name="cmd">The name of the file to run</param>
        /// <param name="args">A string of arguments</param> 
        #endregion
        internal static void ExecuteShell(string cmd, string[] args, bool HookAndWait, string UserName, System.Security.SecureString Password)
        {
        	bool CtrlC = Console.TreatControlCAsInput;
        	Console.TreatControlCAsInput = true;
            System.Diagnostics.Process P = new System.Diagnostics.Process();
            P.StartInfo.FileName = cmd;
            P.StartInfo.UseShellExecute = UserName.Length != 0? false: true;
            P.StartInfo.CreateNoWindow = false;
            P.StartInfo.Arguments = Utils.ConcatWithSpaces(args);
            P.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
            if (UserName != "") P.StartInfo.UserName = UserName;
            if (Password.Length !=0) P.StartInfo.Password = Password;
                System.IO.StreamReader Output = null;
                System.IO.StreamReader Error = null;
                System.IO.StreamWriter Input = null;  
            if (HookAndWait)
            {
                P.StartInfo.RedirectStandardInput = true;
                P.StartInfo.RedirectStandardOutput = true;
                P.StartInfo.RedirectStandardError = true;
                P.StartInfo.UseShellExecute = false;
            }
            try
            {
                P.Start();
                if (HookAndWait)
                {
                    Output = P.StandardOutput;
                    Input = P.StandardInput;
                    Error = P.StandardError;
                    while (!P.HasExited)
                    {
                        if (Console.KeyAvailable)
                        	Input.Write(Console.ReadKey().KeyChar);
                        
                        while (Output.Peek() >= 0)
                            Console.Write((char)Output.Read());
                        
                        while (Error.Peek() >= 0)
                        	Console.Write((char)Error.Read());
                    }
                        while (Output.Peek() >= 0) //Clean up any final output
                        	Console.Write((char)Output.Read());
                        
                        while (Error.Peek() >= 0)
                        	Console.Write((char)Error.Read());
                }
            }
            catch (Exception EX)
            {
                if (HookAndWait)
                    ExecuteShell(cmd, args, false, UserName, Password);
                else
                    Console.WriteLine(EX.ToString());
            }
            finally
            {
                if (HookAndWait)
                {
                    if (Output != null)
                    {
                        Output.Dispose();
                        Output = null;
                    }
                    if (Input != null)
                    {
                        Input.Dispose();
                        Input = null;
                    }
                    if (Error != null) 
                    {
                    	Error.Dispose();
                    	Error = null;
                    }
                }
                Console.TreatControlCAsInput = CtrlC;
            }
        }
    } //End of MainProgram Class
} //End of SharpConsole Namespace
